package org.zjx.analysis.identify;

import com.alibaba.fastjson2.JSON;
import lombok.SneakyThrows;
import org.zjx.command.entity.ParagraphBean;
import org.zjx.command.entity.Font;
import org.zjx.command.enums.FontType;
import org.zjx.command.util.ImageUtil;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class IdentifyFontUtil {
    private static final String identifyResultTag = "identifyResult";
    public static Map<FontType, List<Font>> fontMap = new HashMap<>();

    /**
     * 批量识别多个区域
     */
    public static Map<String, String> batchIdentify(BufferedImage sourceImage,
                                                    Map<String, Rectangle> areas,
                                                    List<Color> colors,
                                                    List<Font> fonts,
                                                    int fontHeight,
                                                    boolean reverse) {
        Map<String, String> results = new ConcurrentHashMap<>();

        // 并行处理所有区域
        areas.entrySet().parallelStream().forEach(entry -> {
            String areaName = entry.getKey();
            Rectangle rect = entry.getValue();

            BufferedImage subImage = ImageUtil.cutImage(sourceImage, rect);
            String result = identifySingleArea(subImage, colors, fonts, fontHeight, reverse);

            results.put(areaName, result);
        });

        return results;
    }

    /**
     * 批量识别多个区域（指定字体类型）
     */
    public static Map<String, String> batchIdentify(BufferedImage sourceImage,
                                                    Map<String, Rectangle> areas,
                                                    List<Color> colors,
                                                    FontType fontType,
                                                    int fontHeight,
                                                    boolean reverse) {
        List<Font> fonts = getFontAttributes(fontType);
        return batchIdentify(sourceImage, areas, colors, fonts, fontHeight, reverse);
    }

    /**
     * 批量识别多个区域（使用默认颜色）
     */
    public static Map<String, String> batchIdentify(BufferedImage sourceImage,
                                                    Map<String, Rectangle> areas,
                                                    List<Font> fonts,
                                                    int fontHeight,
                                                    boolean reverse) {
        // 使用默认颜色（白色）
        List<Color> defaultColors = java.util.Arrays.asList(Color.WHITE);
        return batchIdentify(sourceImage, areas, defaultColors, fonts, fontHeight, reverse);
    }

    /**
     * 批量识别多个区域（使用字体类型和默认颜色）
     */
    public static Map<String, String> batchIdentify(BufferedImage sourceImage,
                                                    Map<String, Rectangle> areas,
                                                    FontType fontType,
                                                    int fontHeight,
                                                    boolean reverse) {
        List<Font> fonts = getFontAttributes(fontType);
        List<Color> defaultColors = java.util.Arrays.asList(Color.WHITE);
        return batchIdentify(sourceImage, areas, defaultColors, fonts, fontHeight, reverse);
    }

    /**
     * 单个区域识别（内部方法）
     */
    private static String identifySingleArea(BufferedImage image, List<Color> colors,
                                             List<Font> fonts, int fontHeight, boolean reverse) {
        ParagraphBean paragraphBean = new ParagraphBean();
        paragraphBean.add(FontUtil.categories(identifyResultTag, colors, image, fonts, fontHeight, reverse));

        if (paragraphBean.getChildren() == null || paragraphBean.getChildren().isEmpty()) {
            return "";
        }

        List<ParagraphBean.Word> words = paragraphBean.getChildren().get(0).getWords();
        if (words == null || words.isEmpty()) {
            return "";
        }

        StringBuilder result = new StringBuilder();
        for (ParagraphBean.Word word : words) {
            String wordName = word.getName();
            if (wordName != null && wordName.contains("null")) {
                // 记录错误/未记录字库图片
                // TODO 记录错误图片
                wordName = wordName.replace("null", "");
            }
            result.append(wordName);
        }

        return result.toString().trim();
    }

    /**
     * 原有的单个识别方法
     */
    public static List<ParagraphBean.Word> identify(BufferedImage image, List<Color> colors,
                                                    List<Font> fonts, int fontHeight, boolean reverse) {
        ParagraphBean paragraphBean = new ParagraphBean();
        paragraphBean.add(FontUtil.categories(identifyResultTag, colors, image, fonts, fontHeight, reverse));

        if (paragraphBean.getChildren() == null || paragraphBean.getChildren().isEmpty()) {
            return null;
        }

        List<ParagraphBean.Word> words = paragraphBean.getChildren().get(0).getWords();
        if (words == null || words.isEmpty()) {
            return null;
        }

        for (ParagraphBean.Word word : words) {
            if (word.getName().contains("null")) {
                // 记录错误/未记录字库图片
                // TODO 记录错误图片
                word.setName(word.getName().replace("null", ""));
                break;
            }
        }

        return words;
    }

    /**
     * 简化的单个识别方法（返回字符串）
     */
    public static String identifyAsString(BufferedImage image, List<Color> colors,
                                          List<Font> fonts, int fontHeight, boolean reverse) {
        List<ParagraphBean.Word> words = identify(image, colors, fonts, fontHeight, reverse);
        if (words == null || words.isEmpty()) {
            return "";
        }

        StringBuilder result = new StringBuilder();
        for (ParagraphBean.Word word : words) {
            result.append(word.getName());
        }

        return result.toString().trim();
    }

    /**
     * 简化的单个识别方法（使用字体类型）
     */
    public static String identifyAsString(BufferedImage image, List<Color> colors,
                                          FontType fontType, int fontHeight, boolean reverse) {
        List<Font> fonts = getFontAttributes(fontType);
        return identifyAsString(image, colors, fonts, fontHeight, reverse);
    }

    @SneakyThrows
    public static List<Font> getFontAttributes(FontType fontType) {
        if (fontMap.containsKey(fontType)) {
            return fontMap.get(fontType);
        }
        String filePath = "fonts/" + fontType.getName() + ".json";
        String json = readFileAsString(Paths.get(filePath));
        List<Font> fonts = JSON.parseArray(json, Font.class);
        if (!fonts.isEmpty()) {
            fontMap.put(fontType, fonts);
        }
        return fonts;
    }

    /**
     * 高效、健壮的文件读取方法（兼容 Java 8）
     */
    private static String readFileAsString(Path path) throws IOException {
        // 检查文件是否存在
        if (!Files.exists(path)) {
            throw new FileNotFoundException("文件不存在: " + path);
        }

        // 检查是否为文件
        if (!Files.isRegularFile(path)) {
            throw new IOException("路径不是文件: " + path);
        }

        // 估计文件大小
        long fileSize = Files.size(path);
        int initialCapacity = (fileSize > 0 && fileSize < Integer.MAX_VALUE) ?
                (int) fileSize : 16384; // 16KB 默认

        try (
                InputStream inputStream = Files.newInputStream(path);
                Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
            StringBuilder content = new StringBuilder(initialCapacity);
            char[] buffer = new char[16384]; // 16KB 缓冲区
            int charsRead;

            while ((charsRead = reader.read(buffer)) != -1) {
                content.append(buffer, 0, charsRead);
            }

            return content.toString();
        } catch (IOException e) {
            throw new IOException("读取文件失败: " + path, e);
        }
    }
}